/*!
 * AJAX module v1.0.0 of jRaiser javascript library
 * http://code.google.com/p/jraiser/
 *
 * Author: Heero.Luo
 * Licensed under MIT license
 *
 * Date: Thu Jun 07 2012 16:50:21 GMT+0800
 */
define(function(require, exports, module) { 'use strict';

var core = require('core/1.0.x/core'),
	qs = require('querystring/1.0.x/querystring'),
	JSON = require('json/1.0.x/json');


// 导入外部js/css
function getFile(url, opts) {
	var head = document.getElementsByTagName('head')[0] || document.documentElement, node;
	
	function onload() {
		var t = this, readyState = t.readyState;
		if (!readyState || readyState === 'loaded' || readyState === 'complete') {
			t.onload = t.onreadystatechange = null;
			opts.onload && opts.onload.call(t);
			if (opts.removable) {
				// 加载js后可以移除节点，但加载css后不可以
				t.parentNode.removeChild(t);
			}
		}
		
		node = null;
	}

	if (opts.nocache) {
		// 添加timestamp防止缓存
		url = qs.append(url, '_=' + +new Date);
	}
	// 添加get参数
	if (opts.data) { url = qs.append(url, opts.data); }
	
	node = document.createElement(opts.nodeName);
	core.extend(node, opts.nodeAttrs);
	if (opts.charset) { node.charset = opts.charset; }
	
	node.onload = node.onreadystatechange = onload;
	node[opts.urlAttr] = url;
	
	// 插入到DOM使其生效
	head.insertBefore(node, head.firstChild);
}

// 创建XMLHttpRequest对象
var createXhr = window.ActiveXObject ? function() {
	try {
		return new ActiveXObject('Microsoft.XMLHTTP');
	} catch (e) { }
} : function() {
	try {
		return new XMLHttpRequest();
	} catch (e) { }
};

core.extend(exports, {
	/**
	 * 创建XMLHttpRequest对象
	 * @return {XMLHttpRequest} XMLHttpRequest对象
	 */
	createXhr : createXhr,
	
	/**
	 * 加载外部样式表文件
	 * @param {String} 文件URL
	 * @param {Function} 加载完成后的回调函数
	 */
	/**
	 * 加载外部样式表文件
	 * @param {String} 文件URL
	 * @param {Object} 加载设置
	 *   onload {Function} 加载完成后的回调函数
	 *   charset {String} 文件编码，当页面编码与样式表编码不同时指定
	 *   data {Object} 附加到URL的GET参数
	 *   nocache {Boolean} 是否在URL中添加timestamp参数（参数名为“_”）以防止缓存
	 *   media {String} 样式对何种设备有效（link标签的media属性）
	 */
	getCss : function(href, opts) {
		if (typeof opts === 'function') {
			opts = { onload : opts };
		}
		
		opts = core.mix({
			nodeName : 'link',
			urlAttr : 'href',
			nodeAttrs : {
				rel : 'stylesheet',
				type : 'text/css'
			}
		}, opts);
		if (opts.media) { opts.nodeAttrs.media = opts.media; }

		getFile(href, opts);
	},
	
	/**
	 * 加载外部脚本文件
	 * @param {String} 脚本文件URL
	 * @param {Function} 加载完成后的回调函数
	 */
	/**
	 * 加载外部脚本文件
	 * @param {String} 文件URL
	 * @param {Object} 加载设置
	 *   onload {Function} 加载完成后的回调函数
	 *   charset {String} 文件编码，当页面编码与样式表编码不同时指定
	 *   data {Object} 附加到URL的GET参数
	 *   nocache {Boolean} 是否在URL中添加timestamp参数（参数名为“_”）以防止缓存
	 */
	getScript : function(src, opts) {
		if (typeof opts === 'function') {
			opts = { onload : opts };
		}
		
		opts = core.mix({
			nodeName : 'script',
			urlAttr : 'src',
			nodeAttrs : {
				type : 'text/javascript',
				async : true
			},
			removable : true
		}, opts);

		getFile(src, opts);
	},
	
	/**
	 * 发送AJAX请求
	 * @param {String} URL（可以把此参数写到设置里面）
	 * @param {Object} 请求设置
	 *   url {String} URL
	 *   method {String} 发送方法，GET、POST或HEAD，默认为GET
	 *   data {Object} 发送的数据
	 *   nocache {Boolean} 是否在URL中添加timestamp参数（参数名为“_”)以防止缓存
	 *   headers {Object} 要设置的HTTP头
	 *   async {Boolean} 是否使用异步方式请求，默认为true
	 *	 timeout {Number} 超时时间
	 *   xhr {XMLHttpRequest} 进行请求的XMLHttpRequest对象，如不指定则自动创建
	 *   dataType {string} 返回的数据格式，json、xml或text，默认为text
	 *   onbeforesend {Function} 发送数据前执行的操作
	 *   onload {Function} 请求完成（不论成功与否）后执行的操作
	 *   onsuccess {Function} 请求成功时执行的操作
	 *   onerror {Function} 请求失败时执行的操作
	 *   onend {Function} 回调处理结束（不论成功与否）后执行的操作
	 */
	send : function(url, opts) {
		// readystatechange回调函数
		function callback(e, statusText) {
			var readyState = xhr.readyState;
			if (readyState !== 4 && !statusText) { return; }
			
			var status = readyState === 4 ? xhr.status : 0, evtType;
			if ( (status >= 200 && status < 300) || status === 1223 || status === 304 ) {
				evtType = 'onsuccess';
			} else if (status || statusText) { // 忽略status为0的情况
				evtType = 'onerror';
				if (!statusText) { statusText = 'error'; }
			}

			if (evtType) {
				var result;
				if (evtType === 'onsuccess') {
					switch (opts.dataType) {
						case 'json':
							var responseText = xhr.responseText;
							if ( responseText.trim() ) {
								try {
									result = JSON.parse(xhr.responseText);
								} catch (e) {
									evtType = 'onerror';
									statusText = 'parsererror';
								}
							}
						break;
						
						case 'xml':
							result = xhr.responseXML;
							if (result && !result.documentElement) { result = null; }
							if (!result) {
								evtType = 'onerror';
								statusText = 'parsererror';
							}
						break;
						
						default:
							result = xhr.responseText;
					}
				}

				// 触发onload事件
				opts.onload && opts.onload.call(window, xhr, statusText);
				
				// 触发回调
				var callback = opts[evtType], args = [xhr, statusText];
				if (evtType === 'onsuccess') { args.unshift(result); }
				callback && callback.apply(window, args);
				
				// 触发onend事件
				opts.onend && opts.onend.call(window, xhr, statusText);
			}
		}
		
		// 重载，允许把url写到opts中
		if (typeof url !== 'string') {
			opts = url;
			url = opts.url;
		}
		
		// 触发beforesend事件，可以在此事件中再次修改opts
		opts.onbeforesend && opts.onbeforesend.call(window, xhr, opts);
		
		// 修正设置值
		var method = (opts.method || 'GET').toUpperCase(),
			async = typeof opts.async === 'boolean' ? async : true,
			data = opts.data,
			headers = opts.headers || { },
			xhr = opts.xhr || createXhr();

		if (data) {
			data = qs.stringify(data);
			switch (method) {
				case 'GET':
					url = qs.append(url, data);
					data = null;
				break;

				case 'POST':
					core.mix(headers, {
						'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
					});
				break;
			}
		}
		
		if (opts.nocache) {
			// 添加timestamp防止缓存
			url = qs.append(url, '_=' + +new Date);
		}

		if (async) {
			// 超时设置仅在异步情况下有效
			if (opts.timeout > 0) {
				setTimeout(function() {
					if (xhr.readyState !== 4) {
						xhr.abort();
						callback.call(xhr, null, 'timeout');
					}
				}, opts.timeout);
			}

			xhr.onreadystatechange = callback;
		}

		if (opts.username) {
			xhr.open(method, url, async, opts.username, opts.password);
		} else {
			xhr.open(method, url, async);
		}

		if (!headers['X-Requested-With']) { headers['X-Requested-With'] = 'XMLHttpRequest'; }
		for (var i in headers) {
			xhr.setRequestHeader(i, headers[i]);
		}

		xhr.send(data || '');

		if (!async) { callback.call(xhr); }

		return xhr;
	}
});

});